feat(attachments): image and PDF attachments for widget and worker - #150
Merged
Conversation
Closes #53. Widget - `attachments` prop / ClaudiusConfig key / `<claudius-chat attachments>` attribute (off by default). Click-to-upload, drag-and-drop, and paste in the composer with client-side type/size/count validation (5 MB, 5 files, JPEG/PNG/GIF/WebP/PDF by default) and localized errors (en/es/fr/de). - Image previews and PDF filename chips in pending state and message bubbles. - ChatApiClient switches to multipart/form-data when a message carries inline bytes; persisted history never stores bytes; stored uploads are swapped for the worker's key + signed URL; rejected uploads roll the message back. Worker - `attachments.ts`: JSON + multipart parsing, allowlist + magic-byte checks, size/count caps, per-request byte budget, image/document content blocks. - `attachment-storage.ts`: passthrough (default) or R2 with retention, HMAC-signed download URLs served by GET /api/attachments/*. - `attachment-quota.ts`: per-IP and per-tenant daily upload quotas in KV. - New error codes: ATTACHMENTS_DISABLED, ATTACHMENT_INVALID, ATTACHMENT_TOO_LARGE, ATTACHMENT_QUOTA_EXCEEDED. Config and docs - Client config schema + CLI validation/snippets for widget.attachments and worker.attachments. - New docs page configuration/attachments.md (limits, storage backends, privacy posture) plus REST, widget, worker, clients, and FAQ updates. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Deploying chat-widget with
|
| Latest commit: |
fa6abc4
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d60291fc.chat-widget-ejc.pages.dev |
| Branch Preview URL: | https://53-file-and-image-attachment.chat-widget-ejc.pages.dev |
Resolve conflicts with the streaming, tool-use, and RAG work that landed on main since the branch was cut: - worker: attachments now flow through a shared prepareAttachments() step used by both POST /api/chat and POST /api/chat/stream; the stream route accepts multipart bodies, reports stored uploads on the done event, and attachment/storage errors are folded into classifyChatError. - worker: validateMessages keeps attachments; both handleChat and streamChat convert them to image/document content blocks via toConversation(). - widget: the client sends the same JSON-or-multipart body to the stream endpoint and surfaces done-event attachments; useChat applies stored attachment metadata after either path and keeps the attachment rollback ahead of the partial-stream error handling. - widget: ChatInput/ChatMessage/ChatWindow/ChatWidget carry both the streaming (stop button, tool chips) and attachment props. - tests: stream-route and stream-client attachment coverage; make the signed-URL tampering assertion deterministic. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
size-limit report 📦
|
The attachment composer (file picker, drag-and-drop, paste, previews), multipart client path, and message-bubble previews add ~3.6 KB gzip to the minified embed bundle, ~4.2 KB gzip to the unminified ESM library, and ~220 B gzip of CSS. New limits are the measured sizes plus the usual 5% headroom, matching the tool-use budget bump. Refs #53 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Lets visitors attach images (JPEG/PNG/GIF/WebP) and PDFs to a message and have Claude analyze them via native
image/documentcontent blocks.Widget
attachmentsoption (React prop,window.ClaudiusConfigkey, and<claudius-chat attachments>attribute). Off by default;trueenables the defaults (5 MB per file, 5 files per message), or pass{ maxSizeBytes, maxCount, allowedTypes }.ChatApiClientswitches tomultipart/form-datawhenever a message carries inline bytes. Persisted history never contains bytes. When the worker stores an upload (R2 mode) the widget keeps the storage key + signed URL instead of the data. A worker-rejected upload rolls the message back and shows a translated error.Worker
attachments.ts: JSON and multipart request parsing, MIME allowlist plus magic-byte verification, per-file / per-message / per-request caps, and Anthropic content-block construction (files first, text last).attachment-storage.ts:passthrough(default, nothing stored) orr2(stored underatt/<tenant>/<uuid>forATTACHMENT_RETENTION_HOURS, with HMAC-signed download URLs served byGET /api/attachments/*, lazy deletion after expiry).attachment-quota.ts: per-IP and per-tenant daily upload-byte quotas in the existingRATE_LIMITKV.ATTACHMENTS_DISABLED(400),ATTACHMENT_INVALID(400),ATTACHMENT_TOO_LARGE(413),ATTACHMENT_QUOTA_EXCEEDED(413 +Retry-After).Config & docs
clients/_schema.json, CLI validation, and snippet generation supportwidget.attachmentsandworker.attachments.configuration/attachments.mdcovering limits, storage backends, and the privacy posture (where files live, how long, who can read them), plus updates to the REST reference, widget/worker/clients config pages, FAQ,wrangler.toml, and.dev.vars.example.Design notes for reviewers:
content.ATTACHMENT_MAX_REQUEST_BYTES) drops history bytes oldest-first.key:exp.Related Issue
Closes #53
Type of Change
Checklist
pnpm testin bothwidget/andworker/) — widget 323, worker 97, scripts 56pnpm lintinwidget/)pnpm format:checkinwidget/)pnpm typecheckinwidget/)cd widget && pnpm build)feat/,fix/,docs/,chore/) — uses the repo's existing<issue>-<slug>patternScreenshots / Notes
docs/node_modules).ChatInput › WithAttachments/WithPendingAttachmentsandChatMessage › WithAttachments.🤖 Generated with Claude Code